﻿Imports NETWeather.gov.weather.graphical
Imports <xmlns:nsfd_meta="http://graphical.weather.gov/xml/DWMLgen/schema/ndfd_meta.xsd">
Imports <xmlns:nsfd_data="http://graphical.weather.gov/xml/DWMLgen/schema/ndfd_data.xsd">






Namespace NOAA
    ''' <summary>
    ''' Weather information related to NOAA's web site.
    ''' This class will use the gov.weather.graphical Web Service namespace.
    ''' </summary>
    ''' <remarks></remarks>
    Public Class NOAA

        ''' <summary>
        ''' The units to use in the query
        ''' </summary>
        Public Enum Units
            ''' <summary>
            ''' Units are in Fahrenheit and Inches
            ''' </summary>
            USStandard

            ''' <summary>
            ''' Units are in Celsius and Meters
            ''' </summary>
            Metric
        End Enum


        ' NOAA Web Service
        ' Web Service URL http://graphical.weather.gov/xml/SOAP_server/ndfdXMLserver.php?wsdl
        Dim NOAAWeather As New gov.weather.graphical.ndfdXML

        ' variables used in Web Service.
        Dim decLatitude As Decimal
        Dim decLongitude As Decimal

        ' Use NOAA's web service to find a latitude/longitude
        ' pair to match a zip code that was entered.
        ' NOAA's web service can only retrieve weather by 
        ' latitude and longitude.
        Private Sub GetLatLong(zipCode As String)

            ' holds the response to the SOAP request.
            Dim strLatLonList As String = NOAAWeather.LatLonListZipCode(zipCode)
            ' XElement used to hold the XML of the SOAP response.
            Dim xml As XElement = XElement.Parse(strLatLonList)

            ' search for the LatLongList element.
            Dim xLatLongList As XElement = xml...<latLonList>.FirstOrDefault

            ' according to NOAA WSDL the zip code list will be
            ' in the format 'LAT, LONG LAT, LONG
            ' take the first Lat, Long from the list.
            If strLatLonList.Trim.Length > 0 Then
                ' separate the latitudes and longitudes.
                Dim strLatLongs() As String = xLatLongList.Value.Split(" "c, ","c)

                ' If we have more then 2 elements in the array
                ' then we will have at least one set of latitude and
                ' longitude to use in weather retrieval
                If strLatLongs.Length >= 2 Then
                    ' fill in the class level variables.
                    Decimal.TryParse(strLatLongs(0), decLatitude)
                    Decimal.TryParse(strLatLongs(1), decLongitude)
                End If

                ' if we have made it this far, then we have a valid
                ' Latitude/Longitude pair.
            End If

        End Sub



        ''' <summary>
        ''' Retrieves weather from NOAA's weather service. It is recommended by NOAA 
        ''' to only query the service every 45-60 minutes since the data is only updated
        ''' in that amount of time.
        ''' </summary>
        ''' <param name="zipCode">A zipCode in 5 digit format (e.g. 12345).</param>
        ''' <param name="units">Units to return results in <see cref="Units"/>. Default: <see cref="Units.USStandard"/></param>
        ''' <param name="numberOfDays">The number of days to query forecast for. Maximum is usually 7. Default: 5 days</param>
        ''' <returns>A List(Of <see cref="WeatherByDayResults"/>) if successful; otherwise nothing.</returns>
        ''' <remarks>To reduce the risk of being banned by NOAA, only call this method once an hour.</remarks>
        Public Function GetWeather(zipCode As String, Optional units As Units = NOAA.Units.USStandard, Optional numberOfDays As Integer = 5) As List(Of WeatherByDayResults)

            ' the results of the query.
            Dim Results As New List(Of WeatherByDayResults)

            Try

                ' calls the web service to retrieve a weather forecast.
                ' e = US Standard units, m=metric units
                ' 24 hourly = every 24 hours, or once for each day

                ' retrieve the latitude and longitude for a zip code.
                GetLatLong(zipCode)

                ' the units to call NOAA's web service
                Dim NOAAUnits As unitType
            
                ' Convert our units to NOAA's units
                Select Case units
                    Case NOAA.Units.USStandard
                        NOAAUnits = unitType.e
                    Case NOAA.Units.Metric
                        NOAAUnits = unitType.m
                End Select

                ' holds the SOAP response.
                Dim strWeather As String

                ' query the web service
                strWeather = NOAAWeather.NDFDgenByDay(decLatitude, decLongitude, Today, NumberOfDays.ToString, NOAAUnits, formatType.Item24hourly)

                ' holds the XML of the SOAP response.
                ' parse the web service response.
                Dim xmlWeather As XDocument = XDocument.Parse(strWeather)

                ' search for the maximum temperatures
                Dim MaxTemparatures = From temps In xmlWeather...<temperature>
                                      Select temps Where temps.@type = "maximum"

                ' search for the minimum temperatures
                Dim MinTemparatures = From temps In xmlWeather...<temperature>
                                      Select temps Where temps.@type = "minimum"

                ' search for the image elements
                Dim Images = From icons In xmlWeather...<icon-link>

                ' finally get the date elements.
                ' use the MaxTemperatures element to get the
                ' time-layout the temperatures are related to.
                Dim MaxTempTimeLayout As String = MaxTemparatures(0).Attribute("time-layout").Value


                ' use the time-layout "key" to get the values.
                ' all the values in this query will only have one layout.
                ' more complex queries will have more.
                Dim TimeLayouts = From timelayout In xmlWeather...<time-layout>
                                   Select timelayout Where timelayout.<layout-key>.FirstOrDefault.Value = MaxTempTimeLayout


                ' fill in the results. The results should be the same as the number of days queried.
                For Days = 0 To NumberOfDays - 1
                    Dim MaxTemp As Integer
                    Dim MinTemp As Integer
                    Dim Image As Uri = Nothing
                    Dim StartTime As DateTime
                    Dim EndTime As DateTime

                    ' Temperatures
                    Integer.TryParse(MaxTemparatures.<value>(Days).Value, MaxTemp)
                    Integer.TryParse(MinTemparatures.<value>(Days).Value, MinTemp)

                    ' Images (if any)
                    If Images IsNot Nothing Then
                        Uri.TryCreate(Images(Days).Value, UriKind.Absolute, Image)
                    End If

                    ' Time periods
                    Date.TryParse(TimeLayouts.FirstOrDefault.<start-valid-time>(Days).Value, StartTime)
                    Date.TryParse(TimeLayouts.FirstOrDefault.<end-valid-time>(Days).Value, EndTime)

                    ' add the variables to the result
                    Results.Add(New WeatherByDayResults With
                            {.MaxTemperature = MaxTemp,
                             .MinTemperature = MinTemp,
                             .Image = Image,
                             .StartTime = StartTime,
                             .EndTime = EndTime
                            })

                Next

                ' if we made it this far, there were no errors, and the results should be filled.

            Catch ex As Exception
                ' there was an error in either the XML or in the
                ' retrieval process.
                MsgBox("There was an error retrieving the weather for " & zipCode & "." & vbCrLf & ex.ToString)
            End Try

            Return Results
        End Function


    End Class


    ''' <summary>
    ''' Holds the <see cref="NOAA.GetWeather"/> results of the web service.
    ''' </summary>
    Public Structure WeatherByDayResults

        Public Property MaxTemperature As Integer
        Public Property MinTemperature As Integer
        Public Property Image As Uri
        Public Property StartTime As DateTime
        Public Property EndTime As DateTime

    End Structure




End Namespace
